﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using WorldBankBBS.Common;

namespace WorldBankBBS.Translators
{
    public class CbmTranslator : ITranslator
    {
        public ISession CurrentSession { get; set; }

        public int ResourceToBytes(byte[] buffer, string resourceName)
        {
            return ResourceToBytes(buffer, 0, buffer.Length, resourceName);
        }

        public int ResourceToBytes(byte[] buffer, int offset, int length, string resourceName)
        {
            ResourceManager rm = new ResourceManager("WorldBankBBS.Resources.StringTable", Assembly.GetAssembly(typeof(WorldBankBBS.Resources.StringTable)));
            return StringToBytes(buffer, offset, length, rm.GetString(resourceName));
        }

        public int StringToBytes(byte[] buffer, string text)
        {
            return StringToBytes(buffer, 0, buffer.Length, text);
        }

        public int StringToBytes(byte[] buffer, string text, params object[] values)
        {
            return StringToBytes(buffer, 0, buffer.Length, text, values);
        }

        public int StringToBytes(byte[] buffer, int offset, int length, string text)
        {
            return StringToBytes(buffer, offset, length, text, new object[0]);
        }

        public int StringToBytes(byte[] buffer, int offset, int length, string text, params object[] values)
        {
            if (buffer == null) throw new ArgumentNullException("buffer");
            if (buffer.Length < offset + length) throw new ArgumentException("Offset and length exceed buffer size.");
            if (text == null) throw new ArgumentNullException("text");

            var result = 0;

            var inTag = false;
            var token = new StringBuilder();

            foreach (char c in text)
            {
                if (c == '{' && !inTag)
                {
                    inTag = true;
                    token.Clear();
                    token.Append(c);
                }
                else if (c == '}' && inTag)
                {
                    inTag = false;
                    token.Append(c);

                    if (token.ToString().StartsWith("{$"))
                    {
                        var value = CurrentSession.GetValue(token.ToString());

                        if (!token.ToString().ToLower().Equals("{$alias}"))
                        {
                            foreach (byte v in value)
                            {
                                if (v == 0) break;
                                var o = TwistToPetscii(new string((char)v, 1));

                                if (o.HasValue)
                                {
                                    buffer[offset + result++] = o.Value;
                                    if (result == length) return result;
                                }
                                else
                                {
                                    buffer[offset + result++] = v;
                                    if (result == length) return result;
                                }
                            }
                        }
                        else
                        {
                            foreach (byte v in value)
                            {
                                if (v == 0) break;

                                buffer[offset + result++] = v;
                                if (result == length) return result;
                            }
                        }
                    }
                    else if (token.ToString().StartsWith("{{"))
                    {
                        var index = Convert.ToInt32(token.ToString().Substring(2, token.Length - 3));

                        if (index >= 0 && index < values.Length)
                        {
                            var sub = new byte[1024];
                            var l = StringToBytes(sub, values[index].ToString());

                            for (int ll = 0; ll < l; ++ll)
                            {
                                buffer[offset + result++] = sub[ll];
                                if (result == length) return result;
                            }
                        }
                    }
                    else if (token.ToString().StartsWith("{%"))
                    {
                        var sub = new byte[1024];
                        var l = ResourceToBytes(sub, token.ToString().Substring(2, token.Length - 3));
                        for (int ll = 0; ll < l; ++ll)
                        {
                            buffer[offset + result++] = sub[ll];
                            if (result == length) return result;
                        }
                    }
                    else
                    {
                        var output = TwistToPetscii(token.ToString());

                        if (output.HasValue)
                        {
                            buffer[offset + result++] = output.Value;
                            if (result == length) return result;
                        }
                    }
                }
                else if (inTag)
                {
                    token.Append(c);
                }
                else
                {
                    var o = TwistToPetscii(new string(c, 1));

                    if (o.HasValue)
                    {
                        buffer[offset + result++] = o.Value;
                        if (result == length) return result;
                    }
                    else
                    {
                        buffer[offset + result++] = (byte)c;
                        if (result == length) return result;
                    }
                }
            }

            return result;
        }

        private byte? TwistToPetscii(string token)
        {
            for (int i = 0; i < 256; ++i)
            {
                if (_twists[i].Token.Equals(token)) return (byte)i;
            }

            return null;
        }

        public string FromBytes(byte[] buffer)
        {
            return FromBytes(buffer, 0, buffer.Length);
        }

        public string FromBytes(byte[] buffer, int offset, int length)
        {
            if (buffer == null) throw new ArgumentNullException("buffer");
            if (buffer.Length < offset + length) throw new ArgumentException("Offset and length exceed buffer size.");

            var chars = new char[length];

            PetsciiEncoder.Petscii.GetPetsciiDecoder().GetChars(buffer, 0, length, chars, 0);

            return new string(chars);
        }

        private Twist[] _twists =
        {
            new Twist {Token = "{0}"},
            new Twist {Token = "{1}"},
            new Twist {Token = "{2}"},
            new Twist {Token = "{STOP}"},
            new Twist {Token = "{4}"},
            new Twist {Token = "{WHITE}"},
            new Twist {Token = "{6}"},
            new Twist {Token = "{7}"},
            new Twist {Token = "{DCS}"},
            new Twist {Token = "{ECS}"},
            new Twist {Token = "{10}"},
            new Twist {Token = "{11}"},
            new Twist {Token = "{12}"},
            new Twist {Token = "{CRLF}"},
            new Twist {Token = "{LCS}"},
            new Twist {Token = "{15}"},

            new Twist {Token = "{16}"},
            new Twist {Token = "{DOWN}"},
            new Twist {Token = "{RVON}"},
            new Twist {Token = "{HOME}"},
            new Twist {Token = "{DELETE}"},
            new Twist {Token = "{21}"},
            new Twist {Token = "{22}"},
            new Twist {Token = "{23}"},
            new Twist {Token = "{24}"},
            new Twist {Token = "{25}"},
            new Twist {Token = "{26}"},
            new Twist {Token = "{27} "},
            new Twist {Token = "{RED}"},
            new Twist {Token = "{RIGHT}"},
            new Twist {Token = "{GREEN}"},
            new Twist {Token = "{BLUE}"},

            new Twist {Token = " "},
            new Twist {Token = "!"},
            new Twist {Token = "{QUOTE}"},
            new Twist {Token = "#"},
            new Twist {Token = "$"},
            new Twist {Token = "%"},
            new Twist {Token = "&"},
            new Twist {Token = "'"},
            new Twist {Token = "("},
            new Twist {Token = ")"},
            new Twist {Token = "*"},
            new Twist {Token = "+"},
            new Twist {Token = ","},
            new Twist {Token = "-"},
            new Twist {Token = "."},
            new Twist {Token = "/"},

            new Twist {Token = "0"},
            new Twist {Token = "1"},
            new Twist {Token = "2"},
            new Twist {Token = "3"},
            new Twist {Token = "4"},
            new Twist {Token = "5"},
            new Twist {Token = "6"},
            new Twist {Token = "7"},
            new Twist {Token = "8"},
            new Twist {Token = "9"},
            new Twist {Token = ":"},
            new Twist {Token = ";"},
            new Twist {Token = "<"},
            new Twist {Token = "="},
            new Twist {Token = ">"},
            new Twist {Token = "?"},

            new Twist {Token = "@"},
            new Twist {Token = "a"},
            new Twist {Token = "b"},
            new Twist {Token = "c"},
            new Twist {Token = "d"},
            new Twist {Token = "e"},
            new Twist {Token = "f"},
            new Twist {Token = "g"},
            new Twist {Token = "h"},
            new Twist {Token = "i"},
            new Twist {Token = "j"},
            new Twist {Token = "k"},
            new Twist {Token = "l"},
            new Twist {Token = "m"},
            new Twist {Token = "n"},
            new Twist {Token = "o"},

            new Twist {Token = "p"},
            new Twist {Token = "q"},
            new Twist {Token = "r"},
            new Twist {Token = "s"},
            new Twist {Token = "t"},
            new Twist {Token = "u"},
            new Twist {Token = "v"},
            new Twist {Token = "w"},
            new Twist {Token = "x"},
            new Twist {Token = "y"},
            new Twist {Token = "z"},
            new Twist {Token = "["},
            new Twist {Token = "{POUND}"},
            new Twist {Token = "]"},
            new Twist {Token = "{UPARROW}"},
            new Twist {Token = "{LEFTARROW}"},

            new Twist {Token = "{96}"},
            new Twist {Token = "A"},
            new Twist {Token = "B"},
            new Twist {Token = "C"},
            new Twist {Token = "D"},
            new Twist {Token = "E"},
            new Twist {Token = "F"},
            new Twist {Token = "G"},
            new Twist {Token = "H"},
            new Twist {Token = "I"},
            new Twist {Token = "J"},
            new Twist {Token = "K"},
            new Twist {Token = "L"},
            new Twist {Token = "M"},
            new Twist {Token = "N"},
            new Twist {Token = "O"},

            new Twist {Token = "P"},
            new Twist {Token = "Q"},
            new Twist {Token = "R"},
            new Twist {Token = "S"},
            new Twist {Token = "T"},
            new Twist {Token = "U"},
            new Twist {Token = "V"},
            new Twist {Token = "W"},
            new Twist {Token = "X"},
            new Twist {Token = "Y"},
            new Twist {Token = "Z"},
            new Twist {Token = "{CROSS}"},
            new Twist {Token = "{124}"},
            new Twist {Token = "{VLINE}"},
            new Twist {Token = "{126}"},
            new Twist {Token = "{127}"},

            new Twist {Token = "{128}"},
            new Twist {Token = "{ORANGE}"},
            new Twist {Token = "{130}"},
            new Twist {Token = "{RUN}"},
            new Twist {Token = "{132}"},
            new Twist {Token = "{F1}"},
            new Twist {Token = "{F3}"},
            new Twist {Token = "{F5}"},
            new Twist {Token = "{F7}"},
            new Twist {Token = "{F2}"},
            new Twist {Token = "{F4}"},
            new Twist {Token = "{F6}"},
            new Twist {Token = "{F8}"},
            new Twist {Token = "{SRETURN}"},
            new Twist {Token = "{UCS}"},
            new Twist {Token = "{143}"},

            new Twist {Token = "{BLACK}"},
            new Twist {Token = "{UP}"},
            new Twist {Token = "{RVOFF}"},
            new Twist {Token = "{CLEAR}"},
            new Twist {Token = "{INSERT}"},
            new Twist {Token = "{BROWN}"},
            new Twist {Token = "{PINK}"},
            new Twist {Token = "{DGRAY}"},
            new Twist {Token = "{MGRAY}"},
            new Twist {Token = "{LGREEN}"},
            new Twist {Token = "{LBLUE}"},
            new Twist {Token = "{LGRAY} "},
            new Twist {Token = "{PURPLE}"},
            new Twist {Token = "{LEFT}"},
            new Twist {Token = "{YELLOW}"},
            new Twist {Token = "{CYAN}"},

            new Twist {Token = "{160}"},
            new Twist {Token = "{161}"},
            new Twist {Token = "{162}"},
            new Twist {Token = "{163}"},
            new Twist {Token = "{164}"},
            new Twist {Token = "{165}"},
            new Twist {Token = "{166}"},
            new Twist {Token = "{167}"},
            new Twist {Token = "{168}"},
            new Twist {Token = "{169}"},
            new Twist {Token = "{170}"},
            new Twist {Token = "{LTEE}"},
            new Twist {Token = "{172}"},
            new Twist {Token = "{LLCORNER}"},
            new Twist {Token = "{URCORNER}"},
            new Twist {Token = "{175}"},

            new Twist {Token = "{ULCORNER}"},
            new Twist {Token = "{BTEE}"},
            new Twist {Token = "{UTEE}"},
            new Twist {Token = "{RTEE}"},
            new Twist {Token = "{180}"},
            new Twist {Token = "{181}"},
            new Twist {Token = "{182}"},
            new Twist {Token = "{183}"},
            new Twist {Token = "{184}"},
            new Twist {Token = "{185}"},
            new Twist {Token = "{CHECK}"},
            new Twist {Token = "{187}"},
            new Twist {Token = "{188}"},
            new Twist {Token = "{189}"},
            new Twist {Token = "{190}"},
            new Twist {Token = "{191}"},

            new Twist {Token = "{HLINE}"},
            new Twist {Token = "{193}"},
            new Twist {Token = "{194}"},
            new Twist {Token = "{195}"},
            new Twist {Token = "{196}"},
            new Twist {Token = "{197}"},
            new Twist {Token = "{198}"},
            new Twist {Token = "{199}"},
            new Twist {Token = "{200}"},
            new Twist {Token = "{201}"},
            new Twist {Token = "{202}"},
            new Twist {Token = "{203}"},
            new Twist {Token = "{204}"},
            new Twist {Token = "{205}"},
            new Twist {Token = "{206}"},
            new Twist {Token = "{207}"},

            new Twist {Token = "{208}"},
            new Twist {Token = "{209}"},
            new Twist {Token = "{210}"},
            new Twist {Token = "{211}"},
            new Twist {Token = "{212}"},
            new Twist {Token = "{213}"},
            new Twist {Token = "{214}"},
            new Twist {Token = "{215}"},
            new Twist {Token = "{216}"},
            new Twist {Token = "{217}"},
            new Twist {Token = "{218}"},
            new Twist {Token = "{219}"},
            new Twist {Token = "{220}"},
            new Twist {Token = "{221}"},
            new Twist {Token = "{222}"},
            new Twist {Token = "{223}"},

            new Twist {Token = "{224}"},
            new Twist {Token = "{225}"},
            new Twist {Token = "{226}"},
            new Twist {Token = "{227}"},
            new Twist {Token = "{228}"},
            new Twist {Token = "{229}"},
            new Twist {Token = "{230}"},
            new Twist {Token = "{231}"},
            new Twist {Token = "{232}"},
            new Twist {Token = "{233}"},
            new Twist {Token = "{234}"},
            new Twist {Token = "{235}"},
            new Twist {Token = "{236}"},
            new Twist {Token = "{237}"},
            new Twist {Token = "{238}"},
            new Twist {Token = "{239}"},

            new Twist {Token = "{240}"},
            new Twist {Token = "{241}"},
            new Twist {Token = "{242}"},
            new Twist {Token = "{243}"},
            new Twist {Token = "{244}"},
            new Twist {Token = "{245}"},
            new Twist {Token = "{246}"},
            new Twist {Token = "{247}"},
            new Twist {Token = "{248}"},
            new Twist {Token = "{249}"},
            new Twist {Token = "{250}"},
            new Twist {Token = "{251}"},
            new Twist {Token = "{252}"},
            new Twist {Token = "{253}"},
            new Twist {Token = "{254}"},
            new Twist {Token = "{255}"}
        };

    }

    internal struct Twist
    {
        public string Token;
    }
}
